﻿////////////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2008 Piers Lawson
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in the
// Software without restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
// Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
////////////////////////////////////////////////////////////////////////////////////

using System;
using System.Linq;
using System.Web;
using System.Collections.Specialized;

namespace ResourcesOverMvc.Web.Mvc.Utilities
{
    public class UriBuilder : System.UriBuilder
    {
        #region QueryItemCollection

        private class QueryItemCollection : NameValueCollection
        {
            private bool _isDirty;

            internal bool IsDirty
            {
                get { return _isDirty; }
                set { _isDirty = value; }
            }

            public override void Add(string name, string value)
            {
                _isDirty = true;
                base.Add(name, value);
            }

            public override void Remove(string name)
            {
                _isDirty = true;
                base.Remove(name);
            }

            public override void Set(string name, string value)
            {
                _isDirty = true;
                base.Set(name, value);
            }
        }

        #endregion

        private QueryItemCollection _queryItems;
        private bool _queryIsDirty;

        public UriBuilder()
        {
        }

        public UriBuilder(string uri) : base(uri)
        {
            _queryIsDirty = true;
        }

        public UriBuilder(Uri uri) : base(uri)
        {
            _queryIsDirty = true;
        }

        public UriBuilder(string schemeName, string hostName) : base(schemeName, hostName) {}
        public UriBuilder(string scheme, string host, int portNumber) : base(scheme, host, portNumber) {}
        public UriBuilder(string scheme, string host, int port, string pathValue) : base(scheme, host, port, pathValue) {}
        public UriBuilder(string scheme, string host, int port, string path, string extraValue) : base(scheme, host, port, path, extraValue) {}

        public NameValueCollection QueryItems
        {
            get
            {
                if (_queryItems == null)
                {
                    _queryItems = new QueryItemCollection();
                }

                SyncQueryItems();
                return _queryItems;
            }
        }

        public new string Query
        {
            get
            {
                SyncQuery();
                return base.Query;
            }

            set
            {
                if (!string.IsNullOrEmpty(value) && value[0] == '?')
                {
                    value = value.Substring(1);
                }

                base.Query = value;
                _queryIsDirty = true;
            }
        }

        public override string ToString()
        {
            SyncQuery();
            return base.ToString();
        }

        public new Uri Uri
        {
            get
            {
                SyncQuery();
                return base.Uri;
            }
        }

        private void SyncQueryItems()
        {
            if (_queryIsDirty)
            {
                CreateItemsFromQuery();
                _queryIsDirty = false;
            }
        }

        private void CreateItemsFromQuery()
        {
            _queryItems.Clear();

            if (base.Query.Length > 0)
            {
                string query = HttpUtility.UrlDecode(base.Query.Substring(1));
                string[] items = query.Split('&');

                foreach (string item in items)
                {
                    if (item.Length > 0)
                    {
                        var namevalue = item.Split('=');

                        _queryItems.Add(namevalue[0], namevalue.Length > 1 ? namevalue[1] : "");
                    }
                }
            }
        }

        private void SyncQuery()
        {
            if (_queryItems != null)
            {
                // First check if queryItems has been cleared (using 
                // QueryItems.Clear()), because this doesn't 
                // update dirty flag!!!

                if (_queryItems.Count == 0)
                {
                    base.Query = "";
                }
                else if (_queryItems.IsDirty)
                {
                    CreateQueryFromItems();
                }

                _queryItems.IsDirty = false;
            }
        }

        private void CreateQueryFromItems()
        {
            var query = "";
            foreach (var key in _queryItems.AllKeys)
            {
                var values = _queryItems.GetValues(key);
                if (values != null)
                {
                    var key1 = key;
                    query = values.Aggregate(query, (current, value) => current + (key1 + "=" + value + "&"));
                }
            }

            if (query.Length > 0)
            {
                query = query.Substring(0, query.Length - 1);
            }

            base.Query = query;
        }
    }
}